package org.bud.rung.netty.util;

import lombok.extern.slf4j.Slf4j;
import org.bud.rung.bit.JobMethod;
import org.bud.rung.bit.cron.*;
import org.bud.rung.constant.Constant;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
 * @author shaun
 * @date 2024/8/10
 * @instruction
 */
@Slf4j
public abstract class Crones {
    private static final String SECOND = "second";
    private static final String MINUTE = "minute";
    private static final String HOUR = "hour";
    private static final String STAR = "start";
    private static final String STEP = "step";
    private static final String DIGIT = "digit";

    private static final Map<Integer, Supplier<CronItem>> itemCache = new LinkedHashMap<>();

    private static final Map<String, Supplier<CronType>> typeCache = new LinkedHashMap<>();

    static {
        itemCache.put(0, CronSecond::new);
        itemCache.put(1, CronMinute::new);
        itemCache.put(2, CronHour::new);

        typeCache.put(STAR, CronStar::new);
        typeCache.put(STEP, CronStep::new);
        typeCache.put(DIGIT, CronDigit::new);
    }

    private static String type(String cron){
        if("*".equals(cron)){
            return STAR;
        }
        if(cron.indexOf("/") > 0){
            return STEP;
        }
        if(cron.matches("\\d+")){
            return DIGIT;
        }
        throw new IllegalArgumentException("not support the cron type for " + cron);
    }

    private static List<CronItem> chainItems(String cron){
        List<CronItem> chains = new ArrayList<>();
        String[] crones = cron.trim().split("\\s+");
        for(int i = 0;i < crones.length;i++){
           CronItem item = itemCache.get(i).get();
           item.type(typeCache.get(type(crones[i])),crones[i]);
           chains.add(item);
           if(i == itemCache.keySet().stream().count() - 1){
               break;
           }
        }
        return chains;
    }

    public static LocalDateTime next(String cron,LocalDateTime pre,boolean initial){
        return next(cron,pre,initial,null);
    }
    public static LocalDateTime next(String cron,LocalDateTime pre,boolean initial,Supplier<Integer> stopCount){
        LocalDateTime last = pre;
        LocalDateTime tmp = pre;
        LocalDateTime before = pre;
        boolean stop = false;
        boolean adder = true;
        boolean copyInitail = initial;
        int i = 0;
        List<CronItem> chains = chainItems(cron);
        int stopCnt = Optional.ofNullable(stopCount)
                .orElse(
                        () -> Long.valueOf(chains.stream()
                                .filter(o -> o.getType() instanceof CronStep)
                                .count()).intValue()
                ).get();
        CronItem item;
        do {
            if(!copyInitail && last.compareTo(pre) > 0){
                stop = true;
                if(initial && i == 0){
                    last = before;
                }
                continue;
            }

            item = chains.get(i);
            item.init(last,copyInitail);
            item.reset(adder,stop);
            item.next();
            stop = item.isStop();
            adder = item.toAdder();
            last = item.last();
            if(!initial && stop){
                continue;
            }
            if(initial && stop){
                before = tmp;
                tmp = last;
            }
            stop = false;
            if(i == chains.size() - 1){
                if(adder){
                    stop = true;
                }
                if(copyInitail){
                    copyInitail = false;
                    i = 0;
                }
                continue;
            }
            if(copyInitail || adder){
                i++;
            } else if(!adder){
                i = 0;
            }
        } while(!stop && i < chains.size());
        if(adder){
            last = last.plusDays(1);
        }
        return last;
    }

    public static void main(String[] args) {
        String cron = "3/15 2/13 18 * * *";
        LocalDateTime pre = LocalDateTime.now();
        boolean initial = true;
        for(int i = 0;i < 100;i++){
            System.out.println(pre);
            pre = next(cron,pre,initial);
            initial = false;
        }
    }
}
